package edu.uwm.cs552;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import java.io.*;

/**
 * A component that displays a hexboard
 */
public class HexBoardPanel extends JPanel implements Scrollable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private double scale;
	private boolean drawMesh;
	private HexBoard board;
	private Set<HexCoordinate> selectedHexCoords;
	
	
	public HexBoardPanel(HexBoard board) {
		this.scale = 10.0d;
		this.drawMesh = false;
		this.board = board;
		this.selectedHexCoords = new HashSet<HexCoordinate>();
		resize();
	}
	
	public void setScale(double s) {
		scale = s;
		resize();
	}
	
	public double getScale() {
		return scale;
	}

	public void setDrawMesh(boolean b) {
		this.drawMesh = b;
		repaint();
	}
	
	public void writeHexBoard(File file) throws FileNotFoundException {
		try {
			PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
			board.write(pw);
			pw.close();
		} catch (Exception e) {
			e.printStackTrace();
			throw new FileNotFoundException();
		}
	}
	
	public void select(HexCoordinate hexCoordinate) {
		if (this.selectedHexCoords == null)
			this.selectedHexCoords = new HashSet<HexCoordinate>();
		
		if (!this.selectedHexCoords.contains(hexCoordinate)) {
			this.selectedHexCoords.add(hexCoordinate);
		
			Graphics g = this.getGraphics();
			g.setXORMode(Color.MAGENTA);
			g.fillPolygon(hexCoordinate.toPolygon(scale));
		}
		
	}
	
	public void toggleSelection(HexCoordinate hexCoordinate) {
		if (this.selectedHexCoords == null)
			this.selectedHexCoords = new HashSet<HexCoordinate>();
		
		if (this.selectedHexCoords.contains(hexCoordinate)) {
			this.selectedHexCoords.remove(hexCoordinate);
		}
		else {
			this.selectedHexCoords.add(hexCoordinate);
		}
		
		Graphics g = this.getGraphics();
		g.setXORMode(Color.MAGENTA);
		g.fillPolygon(hexCoordinate.toPolygon(scale));
	}
	
	public void setTerrainOnSelected(Terrain t) {
		if (selectedHexCoords == null)
			selectedHexCoords = new HashSet<HexCoordinate>();
		
		Iterator<HexCoordinate> it = selectedHexCoords.iterator();
		while (it.hasNext()) {
			board.put(it.next(), t);
		}
		
		repaint();
	}
	
	public void clearSelected() {
		selectedHexCoords = null;
		repaint();
	}

	/**
	 * Set the size of this panel if the background or magnification changes.
	 */
	private void resize() {
		this.setSize(board.getWidth(scale), board.getHeight(scale));
		this.setPreferredSize(this.getSize());
		repaint();
	}
	
	// Methods for handling the selections (clear/toggle/painting)
	
	@Override 
	public void paint(Graphics g) {
		super.paint(g);
		this.board.draw(g, scale, drawMesh);
		
		if (selectedHexCoords != null && !selectedHexCoords.isEmpty()) {
			g.setXORMode(Color.MAGENTA);
		
			Iterator<HexCoordinate> selectedHexIt = this.selectedHexCoords.iterator();
			while (selectedHexIt.hasNext()) {
				g.fillPolygon(selectedHexIt.next().toPolygon(this.scale));
			}
		}
	}
	
	/// The following methods are used to making scroll bars work.
	
	public Dimension getPreferredScrollableViewportSize() {
		return getPreferredSize();
	}

	public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
		if (orientation == SwingConstants.HORIZONTAL) {
			return visibleRect.width - getScrollableUnitIncrement(visibleRect,orientation,direction);
		} else {
			return visibleRect.height - getScrollableUnitIncrement(visibleRect,orientation,direction);
		}
	}

	public boolean getScrollableTracksViewportHeight() {
		return false;
	}

	public boolean getScrollableTracksViewportWidth() {
		return false;
	}

	private static final double SQRT32 = Math.sqrt(3)/2;
	
	public int getScrollableUnitIncrement(Rectangle arg0, int orientation, int direction) {
		// if some clicks the control, we go one hexagon width/height at a time.
		if (orientation == SwingConstants.HORIZONTAL) {
			return (int)scale;
		} else {
			return (int)(scale*SQRT32);
		}
	}
}
